/*-------------------------------------------------------------------------
 *
 * fe-auth-scram.c
 *	   SCRAM 认证的前端（客户端）实现。
 *
 * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 * IDENTIFICATION
 *	  src/interfaces/libpq/fe-auth-scram.c
 *
 *-------------------------------------------------------------------------
 */

#include "postgres_fe.h"

#include "common/base64.h"
#include "common/hmac.h"
#include "common/saslprep.h"
#include "common/scram-common.h"
#include "fe-auth.h"


/* 导出的 SCRAM 回调机制。 */
static void *fc_scram_init(PGconn *fc_conn, const char *fc_password,
						const char *fc_sasl_mechanism);
static void fc_scram_exchange(void *fc_opaq, char *fc_input, int fc_inputlen,
						   char **fc_output, int *fc_outputlen,
						   bool *fc_done, bool *fc_success);
static bool fc_scram_channel_bound(void *fc_opaq);
static void fc_scram_free(void *fc_opaq);

const pg_fe_sasl_mech pg_scram_mech = {
	fc_scram_init,
	fc_scram_exchange,
	fc_scram_channel_bound,
	fc_scram_free
};

/*
 * 用于通过 SASL 协议进行 SCRAM 身份验证的交换消息的状态。
 */
typedef enum
{
	FE_SCRAM_INIT,
	FE_SCRAM_NONCE_SENT,
	FE_SCRAM_PROOF_SENT,
	FE_SCRAM_FINISHED
} fe_scram_state_enum;

typedef struct
{
	fe_scram_state_enum state;

	/* 这些由用户提供 */
	PGconn	   *conn;
	char	   *password;
	char	   *sasl_mechanism;

	/* 我们构造这些 */
	uint8		SaltedPassword[SCRAM_KEY_LEN];
	char	   *client_nonce;
	char	   *client_first_message_bare;
	char	   *client_final_message_without_proof;

	/* 这些来自服务器的第一条消息 */
	char	   *server_first_message;
	char	   *salt;
	int			saltlen;
	int			iterations;
	char	   *nonce;

	/* 这些来自服务器的最终消息 */
	char	   *server_final_message;
	char		ServerSignature[SCRAM_KEY_LEN];
} fe_scram_state;

static bool fc_read_server_first_message(fe_scram_state *fc_state, char *fc_input);
static bool fc_read_server_final_message(fe_scram_state *fc_state, char *fc_input);
static char *fc_build_client_first_message(fe_scram_state *fc_state);
static char *fc_build_client_final_message(fe_scram_state *fc_state);
static bool fc_verify_server_signature(fe_scram_state *fc_state, bool *fc_match,
									const char **fc_errstr);
static bool fc_calculate_client_proof(fe_scram_state *fc_state,
								   const char *fc_client_final_message_without_proof,
								   uint8 *fc_result, const char **fc_errstr);

/*
 * 初始化 SCRAM 交换状态。
 */
static void * fc_scram_init(PGconn *fc_conn,
		   const char *fc_password,
		   const char *fc_sasl_mechanism)
{
	fe_scram_state *fc_state;
	char	   *fc_prep_password;
	pg_saslprep_rc fc_rc;

	Assert(fc_sasl_mechanism != NULL);

	fc_state = (fe_scram_state *) malloc(sizeof(fe_scram_state));
	if (!fc_state)
		return NULL;
	memset(fc_state, 0, sizeof(fe_scram_state));
	fc_state->conn = fc_conn;
	fc_state->state = FE_SCRAM_INIT;
	fc_state->sasl_mechanism = strdup(fc_sasl_mechanism);

	if (!fc_state->sasl_mechanism)
	{
		free(fc_state);
		return NULL;
	}

	/* 如果可能，使用 SASLprep 规范化密码 */
	fc_rc = pg_saslprep(fc_password, &fc_prep_password);
	if (fc_rc == SASLPREP_OOM)
	{
		free(fc_state->sasl_mechanism);
		free(fc_state);
		return NULL;
	}
	if (fc_rc != SASLPREP_SUCCESS)
	{
		fc_prep_password = strdup(fc_password);
		if (!fc_prep_password)
		{
			free(fc_state->sasl_mechanism);
			free(fc_state);
			return NULL;
		}
	}
	fc_state->password = fc_prep_password;

	return fc_state;
}

/*
 * 如果使用了通道绑定并且 SCRAM 交换完成，则返回 true。此值应在成功交换后使用以确定服务器是否已向客户端验证其身份。
 *
 * 请注意，调用者还必须确保交换实际成功。
 */
static bool fc_scram_channel_bound(void *fc_opaq)
{
	fe_scram_state *fc_state = (fe_scram_state *) fc_opaq;

	/* 未进行 SCRAM 交换 */
	if (fc_state == NULL)
		return false;

	/* SCRAM 交换未完成 */
	if (fc_state->state != FE_SCRAM_FINISHED)
		return false;

	/* 未使用通道绑定机制 */
	if (strcmp(fc_state->sasl_mechanism, SCRAM_SHA_256_PLUS_NAME) != 0)
		return false;

	/* 一切正常！ */
	return true;
}

/*
 * 释放 SCRAM 交换状态
 */
static void fc_scram_free(void *fc_opaq)
{
	fe_scram_state *fc_state = (fe_scram_state *) fc_opaq;

	if (fc_state->password)
		free(fc_state->password);
	if (fc_state->sasl_mechanism)
		free(fc_state->sasl_mechanism);

	/* 客户端消息 */
	if (fc_state->client_nonce)
		free(fc_state->client_nonce);
	if (fc_state->client_first_message_bare)
		free(fc_state->client_first_message_bare);
	if (fc_state->client_final_message_without_proof)
		free(fc_state->client_final_message_without_proof);

	/* 来自服务器的第一条消息 */
	if (fc_state->server_first_message)
		free(fc_state->server_first_message);
	if (fc_state->salt)
		free(fc_state->salt);
	if (fc_state->nonce)
		free(fc_state->nonce);

	/* 来自服务器的最终消息 */
	if (fc_state->server_final_message)
		free(fc_state->server_final_message);

	free(fc_state);
}

/*
 * 与后端交换 SCRAM 消息。
 */
static void fc_scram_exchange(void *fc_opaq, char *fc_input, int fc_inputlen,
			   char **fc_output, int *fc_outputlen,
			   bool *fc_done, bool *fc_success)
{
	fe_scram_state *fc_state = (fe_scram_state *) fc_opaq;
	PGconn	   *fc_conn = fc_state->conn;
	const char *fc_errstr = NULL;

	*fc_done = false;
	*fc_success = false;
	*fc_output = NULL;
	*fc_outputlen = 0;

	/*
	 * 检查输入长度是否与输入的字符串长度一致。
	 * 在这之后我们可以忽略 inputlen。
	 */
	if (fc_state->state != FE_SCRAM_INIT)
	{
		if (fc_inputlen == 0)
		{
			appendPQExpBufferStr(&fc_conn->errorMessage,
								 libpq_gettext("malformed SCRAM message (empty message)\n"));
			goto error;
		}
		if (fc_inputlen != strlen(fc_input))
		{
			appendPQExpBufferStr(&fc_conn->errorMessage,
								 libpq_gettext("malformed SCRAM message (length mismatch)\n"));
			goto error;
		}
	}

	switch (fc_state->state)
	{
		case FE_SCRAM_INIT:
			/* 开始 SCRAM 握手，通过发送客户端随机数 */
			*fc_output = fc_build_client_first_message(fc_state);
			if (*fc_output == NULL)
				goto error;

			*fc_outputlen = strlen(*fc_output);
			*fc_done = false;
			fc_state->state = FE_SCRAM_NONCE_SENT;
			break;

		case FE_SCRAM_NONCE_SENT:
			/* 接收盐和服务器随机数，发送响应。 */
			if (!fc_read_server_first_message(fc_state, fc_input))
				goto error;

			*fc_output = fc_build_client_final_message(fc_state);
			if (*fc_output == NULL)
				goto error;

			*fc_outputlen = strlen(*fc_output);
			*fc_done = false;
			fc_state->state = FE_SCRAM_PROOF_SENT;
			break;

		case FE_SCRAM_PROOF_SENT:
			/* 接收服务器签名 */
			if (!fc_read_server_final_message(fc_state, fc_input))
				goto error;

			/*
			 * 验证服务器签名，以确保我们正在与
			 * 真正的服务器通信。
			 */
			if (!fc_verify_server_signature(fc_state, fc_success, &fc_errstr))
			{
				appendPQExpBuffer(&fc_conn->errorMessage,
								  libpq_gettext("could not verify server signature: %s\n"), fc_errstr);
				goto error;
			}

			if (!*fc_success)
			{
				appendPQExpBufferStr(&fc_conn->errorMessage,
									 libpq_gettext("incorrect server signature\n"));
			}
			*fc_done = true;
			fc_state->state = FE_SCRAM_FINISHED;
			break;

		default:
			/* 不应该发生 */
			appendPQExpBufferStr(&fc_conn->errorMessage,
								 libpq_gettext("invalid SCRAM exchange state\n"));
			goto error;
	}
	return;

error:
	*fc_done = true;
	*fc_success = false;
}

/*
 * 读取 SCRAM 消息中属性的值。
 *
 * **input 的缓冲区会被破坏性修改，*input 会
 * 移动到 "attr=value" 字符串及其后面的逗号。
 *
 * 失败时，将错误消息附加到 *errorMessage 并返回 NULL。
 */
static char * fc_read_attr_value(char **fc_input, char fc_attr, PQExpBuffer fc_errorMessage)
{
	char	   *fc_begin = *fc_input;
	char	   *fc_end;

	if (*fc_begin != fc_attr)
	{
		appendPQExpBuffer(fc_errorMessage,
						  libpq_gettext("malformed SCRAM message (attribute \"%c\" expected)\n"),
						  fc_attr);
		return NULL;
	}
	fc_begin++;

	if (*fc_begin != '=')
	{
		appendPQExpBuffer(fc_errorMessage,
						  libpq_gettext("malformed SCRAM message (expected character \"=\" for attribute \"%c\")\n"),
						  fc_attr);
		return NULL;
	}
	fc_begin++;

	fc_end = fc_begin;
	while (*fc_end && *fc_end != ',')
		fc_end++;

	if (*fc_end)
	{
		*fc_end = '\0';
		*fc_input = fc_end + 1;
	}
	else
		*fc_input = fc_end;

	return fc_begin;
}

/*
 * 构建客户端发送的第一条交换消息。
 */
static char * fc_build_client_first_message(fe_scram_state *fc_state)
{
	PGconn	   *fc_conn = fc_state->conn;
	char		fc_raw_nonce[SCRAM_RAW_NONCE_LEN + 1];
	char	   *fc_result;
	int			fc_channel_info_len;
	int			fc_encoded_len;
	PQExpBufferData fc_buf;

	/*
	 * 生成一个“原始”随机数。这通过
	 * base64 编码转换为 ASCII 可打印形式。
	 */
	if (!pg_strong_random(fc_raw_nonce, SCRAM_RAW_NONCE_LEN))
	{
		appendPQExpBufferStr(&fc_conn->errorMessage,
							 libpq_gettext("could not generate nonce\n"));
		return NULL;
	}

	fc_encoded_len = pg_b64_enc_len(SCRAM_RAW_NONCE_LEN);
	/* 别忘了零终止符 */
	fc_state->client_nonce = malloc(fc_encoded_len + 1);
	if (fc_state->client_nonce == NULL)
	{
		appendPQExpBufferStr(&fc_conn->errorMessage,
							 libpq_gettext("out of memory\n"));
		return NULL;
	}
	fc_encoded_len = pg_b64_encode(fc_raw_nonce, SCRAM_RAW_NONCE_LEN,
								fc_state->client_nonce, fc_encoded_len);
	if (fc_encoded_len < 0)
	{
		appendPQExpBufferStr(&fc_conn->errorMessage,
							 libpq_gettext("could not encode nonce\n"));
		return NULL;
	}
	fc_state->client_nonce[fc_encoded_len] = '\0';

	/*
	 * 生成消息。由于后端使用启动数据包提供的值，用户名保持为空。此外，由于此用户名没有使用 SASLprep 进行准备，因此如果它包含 '=' 或 ',' 字符，消息解析将失败。
	 */

	initPQExpBuffer(&fc_buf);

	/*
	 * 首先构建带有通道绑定信息的 gs2 头部。
	 */
	if (strcmp(fc_state->sasl_mechanism, SCRAM_SHA_256_PLUS_NAME) == 0)
	{
		Assert(fc_conn->ssl_in_use);
		appendPQExpBufferStr(&fc_buf, "p=tls-server-end-point");
	}
#ifdef HAVE_PGTLS_GET_PEER_CERTIFICATE_HASH
	else if (fc_conn->channel_binding[0] != 'd' && /* 禁用 */
			 fc_conn->ssl_in_use)
	{
		/*
		 * 客户端支持通道绑定，但认为服务器不支持。
		 */
		appendPQExpBufferChar(&fc_buf, 'y');
	}
#endif
	else
	{
		/*
		 * 客户端不支持通道绑定，或者已禁用它。
		 */
		appendPQExpBufferChar(&fc_buf, 'n');
	}

	if (PQExpBufferDataBroken(fc_buf))
		goto oom_error;

	fc_channel_info_len = fc_buf.len;

	appendPQExpBuffer(&fc_buf, ",,n=,r=%s", fc_state->client_nonce);
	if (PQExpBufferDataBroken(fc_buf))
		goto oom_error;

	/*
	 * 第一条消息的内容需要在没有通道绑定信息的情况下保存。
	 */
	fc_state->client_first_message_bare = strdup(fc_buf.data + fc_channel_info_len + 2);
	if (!fc_state->client_first_message_bare)
		goto oom_error;

	fc_result = strdup(fc_buf.data);
	if (fc_result == NULL)
		goto oom_error;

	termPQExpBuffer(&fc_buf);
	return fc_result;

oom_error:
	termPQExpBuffer(&fc_buf);
	appendPQExpBufferStr(&fc_conn->errorMessage,
						 libpq_gettext("out of memory\n"));
	return NULL;
}

/*
 * 构建客户端发送的最终交换消息。
 */
static char * fc_build_client_final_message(fe_scram_state *fc_state)
{
	PQExpBufferData fc_buf;
	PGconn	   *fc_conn = fc_state->conn;
	uint8		fc_client_proof[SCRAM_KEY_LEN];
	char	   *fc_result;
	int			fc_encoded_len;
	const char *fc_errstr = NULL;

	initPQExpBuffer(&fc_buf);

	/*
	 * 构建不带证明的客户端最终消息。我们需要记住它
	 * 以便在认证的最后一步验证服务器证明。
	 *
	 * 通道绑定标志处理（p/y/n）必须与
	 * build_client_first_message() 一致，因为服务器会检查
	 * 两次使用的是相同的标志。
	 */
	if (strcmp(fc_state->sasl_mechanism, SCRAM_SHA_256_PLUS_NAME) == 0)
	{
#ifdef HAVE_PGTLS_GET_PEER_CERTIFICATE_HASH
		char	   *fc_cbind_data = NULL;
		size_t		fc_cbind_data_len = 0;
		size_t		fc_cbind_header_len;
		char	   *fc_cbind_input;
		size_t		fc_cbind_input_len;
		int			fc_encoded_cbind_len;

		/* 获取服务器的 SSL 证书的哈希数据 */
		fc_cbind_data =
			pgtls_get_peer_certificate_hash(fc_state->conn,
											&fc_cbind_data_len);
		if (fc_cbind_data == NULL)
		{
			/* 错误消息在出错时已设置 */
			termPQExpBuffer(&fc_buf);
			return NULL;
		}

		appendPQExpBufferStr(&fc_buf, "c=");

		/* p=类型,, */
		fc_cbind_header_len = strlen("p=tls-server-end-point,,");
		fc_cbind_input_len = fc_cbind_header_len + fc_cbind_data_len;
		fc_cbind_input = malloc(fc_cbind_input_len);
		if (!fc_cbind_input)
		{
			free(fc_cbind_data);
			goto oom_error;
		}
		memcpy(fc_cbind_input, "p=tls-server-end-point,,", fc_cbind_header_len);
		memcpy(fc_cbind_input + fc_cbind_header_len, fc_cbind_data, fc_cbind_data_len);

		fc_encoded_cbind_len = pg_b64_enc_len(fc_cbind_input_len);
		if (!enlargePQExpBuffer(&fc_buf, fc_encoded_cbind_len))
		{
			free(fc_cbind_data);
			free(fc_cbind_input);
			goto oom_error;
		}
		fc_encoded_cbind_len = pg_b64_encode(fc_cbind_input, fc_cbind_input_len,
										  fc_buf.data + fc_buf.len,
										  fc_encoded_cbind_len);
		if (fc_encoded_cbind_len < 0)
		{
			free(fc_cbind_data);
			free(fc_cbind_input);
			termPQExpBuffer(&fc_buf);
			appendPQExpBufferStr(&fc_conn->errorMessage,
								 "could not encode cbind data for channel binding\n");
			return NULL;
		}
		fc_buf.len += fc_encoded_cbind_len;
		fc_buf.data[fc_buf.len] = '\0';

		free(fc_cbind_data);
		free(fc_cbind_input);
#else
		/*
		 * 选择了通道绑定，但 SSL 库不支持它。
		 * 不应发生。
		 */
		termPQExpBuffer(&fc_buf);
		appendPQExpBufferStr(&fc_conn->errorMessage,
							 "channel binding not supported by this build\n");
		return NULL;
#endif							/* HAVE_PGTLS_GET_PEER_CERTIFICATE_HASH */
	}
#ifdef HAVE_PGTLS_GET_PEER_CERTIFICATE_HASH
	else if (fc_conn->channel_binding[0] != 'd' && /* 禁用 */
			 fc_conn->ssl_in_use)
		appendPQExpBufferStr(&fc_buf, "c=eSws");	/* "y,," 的 base64 */
#endif
	else
		appendPQExpBufferStr(&fc_buf, "c=biws");	/* "n,," 的 base64 */

	if (PQExpBufferDataBroken(fc_buf))
		goto oom_error;

	appendPQExpBuffer(&fc_buf, ",r=%s", fc_state->nonce);
	if (PQExpBufferDataBroken(fc_buf))
		goto oom_error;

	fc_state->client_final_message_without_proof = strdup(fc_buf.data);
	if (fc_state->client_final_message_without_proof == NULL)
		goto oom_error;

	/* 将证明附加到它，以形成客户端最终消息。 */
	if (!fc_calculate_client_proof(fc_state,
								fc_state->client_final_message_without_proof,
								fc_client_proof, &fc_errstr))
	{
		termPQExpBuffer(&fc_buf);
		appendPQExpBuffer(&fc_conn->errorMessage,
						  libpq_gettext("could not calculate client proof: %s\n"),
						  fc_errstr);
		return NULL;
	}

	appendPQExpBufferStr(&fc_buf, ",p=");
	fc_encoded_len = pg_b64_enc_len(SCRAM_KEY_LEN);
	if (!enlargePQExpBuffer(&fc_buf, fc_encoded_len))
		goto oom_error;
	fc_encoded_len = pg_b64_encode((char *) fc_client_proof,
								SCRAM_KEY_LEN,
								fc_buf.data + fc_buf.len,
								fc_encoded_len);
	if (fc_encoded_len < 0)
	{
		termPQExpBuffer(&fc_buf);
		appendPQExpBufferStr(&fc_conn->errorMessage,
							 libpq_gettext("could not encode client proof\n"));
		return NULL;
	}
	fc_buf.len += fc_encoded_len;
	fc_buf.data[fc_buf.len] = '\0';

	fc_result = strdup(fc_buf.data);
	if (fc_result == NULL)
		goto oom_error;

	termPQExpBuffer(&fc_buf);
	return fc_result;

oom_error:
	termPQExpBuffer(&fc_buf);
	appendPQExpBufferStr(&fc_conn->errorMessage,
						 libpq_gettext("out of memory\n"));
	return NULL;
}

/*
 * 读取来自服务器的第一条交换消息。
 */
static bool fc_read_server_first_message(fe_scram_state *fc_state, char *fc_input)
{
	PGconn	   *fc_conn = fc_state->conn;
	char	   *fc_iterations_str;
	char	   *fc_endptr;
	char	   *fc_encoded_salt;
	char	   *fc_nonce;
	int			fc_decoded_salt_len;

	fc_state->server_first_message = strdup(fc_input);
	if (fc_state->server_first_message == NULL)
	{
		appendPQExpBufferStr(&fc_conn->errorMessage,
							 libpq_gettext("out of memory\n"));
		return false;
	}

	/* 解析消息 */
	fc_nonce = fc_read_attr_value(&fc_input, 'r',
							&fc_conn->errorMessage);
	if (fc_nonce == NULL)
	{
		/* read_attr_value() 已附加错误字符串 */
		return false;
	}

	/* 立即验证服务器是否使用了我们的 nonce 部分 */
	if (strlen(fc_nonce) < strlen(fc_state->client_nonce) ||
		memcmp(fc_nonce, fc_state->client_nonce, strlen(fc_state->client_nonce)) != 0)
	{
		appendPQExpBufferStr(&fc_conn->errorMessage,
							 libpq_gettext("invalid SCRAM response (nonce mismatch)\n"));
		return false;
	}

	fc_state->nonce = strdup(fc_nonce);
	if (fc_state->nonce == NULL)
	{
		appendPQExpBufferStr(&fc_conn->errorMessage,
							 libpq_gettext("out of memory\n"));
		return false;
	}

	fc_encoded_salt = fc_read_attr_value(&fc_input, 's', &fc_conn->errorMessage);
	if (fc_encoded_salt == NULL)
	{
		/* read_attr_value() 已附加错误字符串 */
		return false;
	}
	fc_decoded_salt_len = pg_b64_dec_len(strlen(fc_encoded_salt));
	fc_state->salt = malloc(fc_decoded_salt_len);
	if (fc_state->salt == NULL)
	{
		appendPQExpBufferStr(&fc_conn->errorMessage,
							 libpq_gettext("out of memory\n"));
		return false;
	}
	fc_state->saltlen = pg_b64_decode(fc_encoded_salt,
								   strlen(fc_encoded_salt),
								   fc_state->salt,
								   fc_decoded_salt_len);
	if (fc_state->saltlen < 0)
	{
		appendPQExpBufferStr(&fc_conn->errorMessage,
							 libpq_gettext("malformed SCRAM message (invalid salt)\n"));
		return false;
	}

	fc_iterations_str = fc_read_attr_value(&fc_input, 'i', &fc_conn->errorMessage);
	if (fc_iterations_str == NULL)
	{
		/* read_attr_value() 已附加错误字符串 */
		return false;
	}
	fc_state->iterations = strtol(fc_iterations_str, &fc_endptr, 10);
	if (*fc_endptr != '\0' || fc_state->iterations < 1)
	{
		appendPQExpBufferStr(&fc_conn->errorMessage,
							 libpq_gettext("malformed SCRAM message (invalid iteration count)\n"));
		return false;
	}

	if (*fc_input != '\0')
		appendPQExpBufferStr(&fc_conn->errorMessage,
							 libpq_gettext("malformed SCRAM message (garbage at end of server-first-message)\n"));

	return true;
}

/*
 * 读取来自服务器的最终交换消息。
 */
static bool fc_read_server_final_message(fe_scram_state *fc_state, char *fc_input)
{
	PGconn	   *fc_conn = fc_state->conn;
	char	   *fc_encoded_server_signature;
	char	   *fc_decoded_server_signature;
	int			fc_server_signature_len;

	fc_state->server_final_message = strdup(fc_input);
	if (!fc_state->server_final_message)
	{
		appendPQExpBufferStr(&fc_conn->errorMessage,
							 libpq_gettext("out of memory\n"));
		return false;
	}

	/* 检查错误结果。 */
	if (*fc_input == 'e')
	{
		char	   *fc_errmsg = fc_read_attr_value(&fc_input, 'e',
											 &fc_conn->errorMessage);

		if (fc_errmsg == NULL)
		{
			/* read_attr_value() 已附加错误消息 */
			return false;
		}
		appendPQExpBuffer(&fc_conn->errorMessage,
						  libpq_gettext("error received from server in SCRAM exchange: %s\n"),
						  fc_errmsg);
		return false;
	}

	/* 解析消息。 */
	fc_encoded_server_signature = fc_read_attr_value(&fc_input, 'v',
											   &fc_conn->errorMessage);
	if (fc_encoded_server_signature == NULL)
	{
		/* read_attr_value() 已附加错误消息 */
		return false;
	}

	if (*fc_input != '\0')
		appendPQExpBufferStr(&fc_conn->errorMessage,
							 libpq_gettext("malformed SCRAM message (garbage at end of server-final-message)\n"));

	fc_server_signature_len = pg_b64_dec_len(strlen(fc_encoded_server_signature));
	fc_decoded_server_signature = malloc(fc_server_signature_len);
	if (!fc_decoded_server_signature)
	{
		appendPQExpBufferStr(&fc_conn->errorMessage,
							 libpq_gettext("out of memory\n"));
		return false;
	}

	fc_server_signature_len = pg_b64_decode(fc_encoded_server_signature,
										 strlen(fc_encoded_server_signature),
										 fc_decoded_server_signature,
										 fc_server_signature_len);
	if (fc_server_signature_len != SCRAM_KEY_LEN)
	{
		free(fc_decoded_server_signature);
		appendPQExpBufferStr(&fc_conn->errorMessage,
							 libpq_gettext("malformed SCRAM message (invalid server signature)\n"));
		return false;
	}
	memcpy(fc_state->ServerSignature, fc_decoded_server_signature, SCRAM_KEY_LEN);
	free(fc_decoded_server_signature);

	return true;
}

/*
 * 计算客户端证明，作为客户端发送的最终交换消息的一部分。
 * 成功时返回 true，失败时返回 false，*errstr 指向错误详情的消息。
 */
static bool fc_calculate_client_proof(fe_scram_state *fc_state,
					   const char *fc_client_final_message_without_proof,
					   uint8 *fc_result, const char **fc_errstr)
{
	uint8		fc_StoredKey[SCRAM_KEY_LEN];
	uint8		fc_ClientKey[SCRAM_KEY_LEN];
	uint8		fc_ClientSignature[SCRAM_KEY_LEN];
	int			fc_i;
	pg_hmac_ctx *fc_ctx;

	fc_ctx = pg_hmac_create(PG_SHA256);
	if (fc_ctx == NULL)
	{
		*fc_errstr = pg_hmac_error(NULL);	/* 返回 OOM */
		return false;
	}

	/*
	 * 计算盐值密码，并将其存储在 'state' 中，以便我们可以在
	 * 验证服务器签名时重用它。
	 */
	if (scram_SaltedPassword(fc_state->password, fc_state->salt, fc_state->saltlen,
							 fc_state->iterations, fc_state->SaltedPassword,
							 fc_errstr) < 0 ||
		scram_ClientKey(fc_state->SaltedPassword, fc_ClientKey, fc_errstr) < 0 ||
		scram_H(fc_ClientKey, SCRAM_KEY_LEN, fc_StoredKey, fc_errstr) < 0)
	{
		/* errstr 在这里已填充 */
		pg_hmac_free(fc_ctx);
		return false;
	}

	if (pg_hmac_init(fc_ctx, fc_StoredKey, SCRAM_KEY_LEN) < 0 ||
		pg_hmac_update(fc_ctx,
					   (uint8 *) fc_state->client_first_message_bare,
					   strlen(fc_state->client_first_message_bare)) < 0 ||
		pg_hmac_update(fc_ctx, (uint8 *) ",", 1) < 0 ||
		pg_hmac_update(fc_ctx,
					   (uint8 *) fc_state->server_first_message,
					   strlen(fc_state->server_first_message)) < 0 ||
		pg_hmac_update(fc_ctx, (uint8 *) ",", 1) < 0 ||
		pg_hmac_update(fc_ctx,
					   (uint8 *) fc_client_final_message_without_proof,
					   strlen(fc_client_final_message_without_proof)) < 0 ||
		pg_hmac_final(fc_ctx, fc_ClientSignature, sizeof(fc_ClientSignature)) < 0)
	{
		*fc_errstr = pg_hmac_error(fc_ctx);
		pg_hmac_free(fc_ctx);
		return false;
	}

	for (fc_i = 0; fc_i < SCRAM_KEY_LEN; fc_i++)
		fc_result[fc_i] = fc_ClientKey[fc_i] ^ fc_ClientSignature[fc_i];

	pg_hmac_free(fc_ctx);
	return true;
}

/*
 * 验证服务器签名，该签名作为从服务器接收的最终交换消息的一部分。
 * *match 跟踪服务器签名是否匹配。返回 true 表示服务器签名已验证，
 * false 则表示处理错误，*errstr 指向错误详情的消息。
 */
static bool fc_verify_server_signature(fe_scram_state *fc_state, bool *fc_match,
						const char **fc_errstr)
{
	uint8		fc_expected_ServerSignature[SCRAM_KEY_LEN];
	uint8		fc_ServerKey[SCRAM_KEY_LEN];
	pg_hmac_ctx *fc_ctx;

	fc_ctx = pg_hmac_create(PG_SHA256);
	if (fc_ctx == NULL)
	{
		*fc_errstr = pg_hmac_error(NULL);	/* 返回 OOM */
		return false;
	}

	if (scram_ServerKey(fc_state->SaltedPassword, fc_ServerKey, fc_errstr) < 0)
	{
		/* errstr 已填写 */
		pg_hmac_free(fc_ctx);
		return false;
	}

	/* 计算 ServerSignature */
	if (pg_hmac_init(fc_ctx, fc_ServerKey, SCRAM_KEY_LEN) < 0 ||
		pg_hmac_update(fc_ctx,
					   (uint8 *) fc_state->client_first_message_bare,
					   strlen(fc_state->client_first_message_bare)) < 0 ||
		pg_hmac_update(fc_ctx, (uint8 *) ",", 1) < 0 ||
		pg_hmac_update(fc_ctx,
					   (uint8 *) fc_state->server_first_message,
					   strlen(fc_state->server_first_message)) < 0 ||
		pg_hmac_update(fc_ctx, (uint8 *) ",", 1) < 0 ||
		pg_hmac_update(fc_ctx,
					   (uint8 *) fc_state->client_final_message_without_proof,
					   strlen(fc_state->client_final_message_without_proof)) < 0 ||
		pg_hmac_final(fc_ctx, fc_expected_ServerSignature,
					  sizeof(fc_expected_ServerSignature)) < 0)
	{
		*fc_errstr = pg_hmac_error(fc_ctx);
		pg_hmac_free(fc_ctx);
		return false;
	}

	pg_hmac_free(fc_ctx);

	/* 签名已处理，因此现在检查它之后的内容 */
	if (memcmp(fc_expected_ServerSignature, fc_state->ServerSignature, SCRAM_KEY_LEN) != 0)
		*fc_match = false;
	else
		*fc_match = true;

	return true;
}

/*
 * 构建一个新的 SCRAM 密钥。
 *
 * 出错时返回 NULL，并将 *errstr 设置为指向错误详情的消息。
 */
char * pg_fe_scram_build_secret(const char *fc_password, const char **fc_errstr)
{
	char	   *fc_prep_password;
	pg_saslprep_rc fc_rc;
	char		fc_saltbuf[SCRAM_DEFAULT_SALT_LEN];
	char	   *fc_result;

	/*
	 * 使用 SASLprep 对密码进行规范化。如果这不起作用，因为
	 * 密码不是有效的 UTF-8 或包含禁止字符，则只需
	 * 继续使用原始密码。（详见 auth-scram.c 顶部的注释。）
	 */
	fc_rc = pg_saslprep(fc_password, &fc_prep_password);
	if (fc_rc == SASLPREP_OOM)
	{
		*fc_errstr = libpq_gettext("out of memory");
		return NULL;
	}
	if (fc_rc == SASLPREP_SUCCESS)
		fc_password = (const char *) fc_prep_password;

	/* 生成随机盐值 */
	if (!pg_strong_random(fc_saltbuf, SCRAM_DEFAULT_SALT_LEN))
	{
		*fc_errstr = libpq_gettext("could not generate random salt");
		if (fc_prep_password)
			free(fc_prep_password);
		return NULL;
	}

	fc_result = scram_build_secret(fc_saltbuf, SCRAM_DEFAULT_SALT_LEN,
								SCRAM_DEFAULT_ITERATIONS, fc_password,
								fc_errstr);

	if (fc_prep_password)
		free(fc_prep_password);

	return fc_result;
}
